#include "mDebug.h"
#include "CMap.h"

#include <string.h>
#include <stdio.h>

#define PRINT_DEBUG_LOG_WITH_DEEP(fmt, ...) { \
	int _i_ = 0; \
	char strDeep[512] = {0}; \
	for(; _i_ < deepth * 4; ++_i_) { \
		strDeep[_i_] = ' '; \
	} \
    PRINT_DEBUG_LOG("%s" fmt, strDeep, ##__VA_ARGS__); \
}

FILE* _md_log_f = NULL;
char _md_log_buf[4096];

const char* get_tt_name(int tt) {
    switch (tt) {
    case LUA_TNIL:
        return "LUA_TNIL";
    case LUA_TBOOLEAN:
        return "LUA_TBOOLEAN";
    case LUA_TLIGHTUSERDATA:
        return "LUA_TLIGHTUSERDATA";
    case LUA_TNUMBER:
        return "LUA_TNUMBER";
    case LUA_TSTRING:
        return "LUA_TSTRING";
    case LUA_TTABLE:
        return "LUA_TTABLE";
    case LUA_TFUNCTION:
        return "LUA_TFUNCTION";
    case LUA_TUSERDATA:
        return "LUA_TUSERDATA";
    case LUA_TTHREAD:
        return "LUA_TTHREAD";
    default:
        return "UNKNOWN";
    }
}

typedef struct {
    void** arr;
    int all_cnt;
    int reg_cnt; //已登录个数
} OUT_INFO; 
OUT_INFO gTablOutputDone;
OUT_INFO gTablOutputTODO;
CMap gAddr2Fn;

#define ADDR2FN_ST_UNINITED          1
#define ADDR2FN_ST_READY             2

static addr2fn_st = ADDR2FN_ST_UNINITED;
int sub_parse_addr2fn(char* buf) {
    int cnt = 0;
    void* unuse;
    void* addr;
    char funcName[1024] = { 0 };
    char fileName[1024] = { 0 };

    switch (addr2fn_st) {
    case ADDR2FN_ST_UNINITED:
        if (0 == strcmp("  Address         Publics by Value              Rva+Base       Lib:Object", buf)) {
            addr2fn_st = ADDR2FN_ST_READY;
        }
        return 0;
    case ADDR2FN_ST_READY:
        cnt = sscanf(buf, " 0002:%x       _%s           %x f   %s.obj", (unsigned int *)&unuse, funcName, (unsigned int *)&addr, fileName);
        if (cnt != 4) {
            return 0;
        }
        gAddr2Fn.insert(&gAddr2Fn, addr, _strdup(funcName));
        return 0;
    default:
        return 0;
    }
    return 0;
}
static int make_addr2fn_map() {
    char buf[1024 * 10] = { 0 };
    FILE* f = fopen("lvd.map", "r");
    if (f == NULL) {
        fprintf(stderr, "map file was not found.\n");
        return -1;
    }

    while (!feof(f)) {
        if (fgets(buf, sizeof(buf), f)) {
            int i = strlen(buf);
            while (buf[i - 1] == '\r' || buf[i - 1] == '\n') {
                buf[i - 1] = '\0';
                i -= 1;
            }
            sub_parse_addr2fn(buf);
            memset(buf, sizeof(buf), 0);
        }
    }
    fclose(f);
    return 0;
}

int init_mdebug() {
    memset(&gTablOutputDone, 0, sizeof(gTablOutputDone));
    gTablOutputDone.all_cnt = 2;
    gTablOutputDone.arr = (void**)malloc(sizeof(void*) * gTablOutputDone.all_cnt);
    memset(gTablOutputDone.arr, 0, sizeof(void*) * gTablOutputDone.all_cnt);

    memset(&gTablOutputTODO, 0, sizeof(gTablOutputTODO));
    gTablOutputTODO.all_cnt = 2;
    gTablOutputTODO.arr = (void**)malloc(sizeof(void*) * gTablOutputTODO.all_cnt);
    memset(gTablOutputTODO.arr, 0, sizeof(void*) * gTablOutputTODO.all_cnt);

    construct_map(&gAddr2Fn, 0);
    addr2fn_st = ADDR2FN_ST_UNINITED;
    make_addr2fn_map();
    return 0;
}

int un_init_mdebug() {
    free(gTablOutputDone.arr);
    memset(&gTablOutputDone, 0, sizeof(gTablOutputDone));

    free(gTablOutputTODO.arr);
    memset(&gTablOutputTODO, 0, sizeof(gTablOutputTODO));

//    free(gAddr2Fn.arr);
    addr2fn_st = ADDR2FN_ST_UNINITED;
    return 0;
}

char* get_func_name_by_addr(void* addr) {
    char* funcName = gAddr2Fn.find(&gAddr2Fn, addr);
    if (0 == funcName) return "FUNCTION NOT FOUND!";
    return funcName;
}

int is_element_in(OUT_INFO* mng, void* v) {
    for (int i = 0; i < mng->reg_cnt; ++i) {
        if (v == mng->arr[i]) {
            return 1; // output done
        }
    }
    return 0; // not done
}

int add_element(OUT_INFO* mng, void* v) {
    if (mng->reg_cnt >= mng->all_cnt) {
        int new_all_cnt = mng->all_cnt * 2;
        void** newArr = (void**)malloc(sizeof(void*) * new_all_cnt);
        memset(newArr, 0, sizeof(void*) * new_all_cnt);
        memcpy(newArr, mng->arr, sizeof(void*) * mng->all_cnt);
        free(mng->arr);
        mng->arr = newArr;
        mng->all_cnt = new_all_cnt;
    }
    mng->arr[mng->reg_cnt++] = v;
    return 0;
}

int get_logic_pos(Table* t, Node* nd, int* main_pos, int* next_pos) {
    int mpos = 0;
    if (nd->i_key.tvk.tt == LUA_TSTRING) {
        TString* node_key = &nd->i_key.nk.value.gc->ts;
        mpos = node_key->tsv.hash % twoto(t->lsizenode);
    }
    else if(nd->i_key.tvk.tt == LUA_TNUMBER) {
        unsigned int a[sizeof(lua_Number) / sizeof(int)];
        lua_Number n = nd->i_key.tvk.value.n + 1;
        memcpy(a, &n, sizeof(a));
        for (int i = 1; i < (sizeof(lua_Number) / sizeof(int)); i++) a[0] += a[i];
        mpos = a[0] % ((twoto(t->lsizenode) - 1) | 1); //see ltable.c hashmod
    }
    Node* it = &t->node[mpos];
    int sub_pos = 0;
    while (it != nd) {
        ++sub_pos;
        it = it->i_key.nk.next;
    }
    *main_pos = mpos;
    *next_pos = sub_pos;
    return 0;
}

int sub_rescure_dump_table(Table* t, int deepth) {
    int i = 0;
    if (is_element_in(&gTablOutputDone, t)) {
        PRINT_DEBUG_LOG_WITH_DEEP("the value is a Table that has outputed. Table addr:0x%08X.\n", (int)t);
        return -1;
    }
    add_element(&gTablOutputDone, t);
    PRINT_DEBUG_LOG_WITH_DEEP("addr=0x%08X type:TABLE lsizenode:%d, nodeCount:%d\n", (int)t, t->lsizenode, twoto(t->lsizenode));

    //dump the metatable
    if (t->metatable) {
        PRINT_DEBUG_LOG_WITH_DEEP("[metatable]:\n");
        sub_rescure_dump_table(t->metatable, deepth + 1);
    } else {
        PRINT_DEBUG_LOG_WITH_DEEP("[metatable]:NONE\n");
    }

    //dump the nodes
    PRINT_DEBUG_LOG_WITH_DEEP("[node]:\n");
    for (i = 0; i < twoto(t->lsizenode); ++i) {
        Node* nd = &t->node[i];
        int mpos = 0;
        int sub_pos = 0;
        if (nd->i_key.tvk.tt == LUA_TNIL) {
            PRINT_DEBUG_LOG_WITH_DEEP("  node[%d]  is [FREE]\n", i);
            continue;
        }
        get_logic_pos(t, nd, &mpos, &sub_pos);
        int next_node_idx = 0;
        if (NULL == nd->i_key.nk.next) {
            PRINT_DEBUG_LOG_WITH_DEEP("  node[%d]  logic_pos:[%d][%d]   next -> NULL\n", i, mpos, sub_pos);
        } else {
            PRINT_DEBUG_LOG_WITH_DEEP("  node[%d]  logic_pos:[%d][%d]   next -> node[%d]\n", i, mpos, sub_pos, nd->i_key.nk.next - &t->node[0]);
        }
        PRINT_DEBUG_LOG_WITH_DEEP("    [key]  :");
        MD_DUMP_LUA_OBJ((TValue*)&nd->i_key.nk.value, deepth);
        PRINT_DEBUG_LOG_WITH_DEEP("    [value]:");
        MD_DUMP_LUA_OBJ((TValue*)&nd->i_val, deepth);
        PRINT_DEBUG_LOG_WITH_DEEP("\n");
    }
    PRINT_DEBUG_LOG_WITH_DEEP("\n");
    return 0;
}

int MD_DUMP_LUA_OBJ(TValue* o, int deepth) {
    if (NULL == o) {
        PRINT_DEBUG_LOG_WITH_DEEP("NULL POINTER!!!!!!!!!!!!!!!!!!!!!!!!!\n");
        return -1;
    }
	++deepth;
	switch (o->tt) {
	case LUA_TNIL:
		PRINT_DEBUG_LOG_WITH_DEEP("type:NIL\n");
		break;
	case LUA_TBOOLEAN:
		PRINT_DEBUG_LOG_WITH_DEEP("type:BOOL val:%d\n", o->value.b);
		break;
    case LUA_TLIGHTUSERDATA:
		PRINT_DEBUG_LOG_WITH_DEEP("addr=0x%08X type:LIGHTUSERDATA val:TODO\n", (int)o->value.gc);
		break;
	case LUA_TNUMBER:
		PRINT_DEBUG_LOG_WITH_DEEP("type:NUMBER val:%lf\n", o->value.n);
		break;
	case LUA_TSTRING: {
		    TString* ts = rawgco2ts(o->value.gc);
		    PRINT_DEBUG_LOG_WITH_DEEP("addr=0x%08X type:STRING hash:0x%08X slen:%d val:%s\n", (int)o->value.gc, ts->tsv.hash, ts->tsv.len, getstr(ts));
    	}
		break;
    case LUA_TTABLE: 
        sub_rescure_dump_table(&o->value.gc->h, deepth);
		break;
    case LUA_TFUNCTION:
		PRINT_DEBUG_LOG_WITH_DEEP("addr=0x%08X type:FUNC val:0x%08X\n", (int)o->value.gc, (int)o->value.gc->cl.c.f);
        {
            Closure* cl = &o->value.gc->cl;
            if (cl->c.env) {
                PRINT_DEBUG_LOG_WITH_DEEP("    [env] NONE.\n");
            } else {
                PRINT_DEBUG_LOG_WITH_DEEP("    [env]\n");
                sub_rescure_dump_table(cl->c.env, deepth + 2);
            }

            if (cl->c.isC) {
                PRINT_DEBUG_LOG_WITH_DEEP("    isC=true(CClosure) nupvalues=%d. funcName:%s(0x%x)\n", cl->c.nupvalues, get_func_name_by_addr(cl->c.f), (unsigned int)cl->c.f);
                for (int i = 0; i < cl->c.nupvalues; ++i) {
                    PRINT_DEBUG_LOG_WITH_DEEP("    upvalues[%d]\n", i);
                    MD_DUMP_LUA_OBJ(&cl->c.upvalue[i], deepth + 2);                    
                }
                break;
            }
            PRINT_DEBUG_LOG_WITH_DEEP("    isC=flase(LClosure) nupvalues=%d.\n", cl->l.nupvalues);
            for (int i = 0; i < cl->l.nupvalues; ++i) {
                PRINT_DEBUG_LOG_WITH_DEEP("    upvals[%d]\n", i);
                MD_DUMP_LUA_OBJ(cl->l.upvals[i]->v, deepth + 2);
            }
        }
		break;
    case LUA_TUSERDATA: {
            if (is_element_in(&gTablOutputDone, o->value.gc)) {
                PRINT_DEBUG_LOG_WITH_DEEP("the value is a USERDATA that has outputed. addr:0x%08X.\n", (int)o->value.gc);
                return 0;
            }
            add_element(&gTablOutputDone, o->value.gc);

            Udata* u = &o->value.gc->u;
		    PRINT_DEBUG_LOG_WITH_DEEP("addr=0x%08X type:USERDATA len=%d, val:\n", (int)o->value.gc, u->uv.len);
           
            PRINT_DEBUG_LOG_WITH_DEEP("    [envirment]:\n");
            sub_rescure_dump_table(u->uv.env, deepth + 2);

            PRINT_DEBUG_LOG_WITH_DEEP("    [metatable]:\n");
            sub_rescure_dump_table(u->uv.metatable, deepth + 2);
        }
		break;
    case LUA_TTHREAD:
		PRINT_DEBUG_LOG_WITH_DEEP("addr=0x%08X type:THREAD val:TODO\n", (int)o->value.gc);
		break;
	default:
		PRINT_DEBUG_LOG_WITH_DEEP("addr=0x%08X type:UNKNOWN!!\n", (int)o->value.gc);
		break;
	}
	return 0;
}




int sub_rescure_dump_table_easy(Table* t, int deepth) {
    int i = 0;
    if (is_element_in(&gTablOutputDone, t)) {
        PRINT_DEBUG_LOG_WITH_DEEP("the value is a Table that has outputed. Table addr:0x%08X.\n", (int)t);
        return -1;
    }
    add_element(&gTablOutputDone, t);
    PRINT_DEBUG_LOG_WITH_DEEP("addr=0x%08X type:TABLE lsizenode:%d, nodeCount:%d\n", (int)t, t->lsizenode, twoto(t->lsizenode));

    //dump the metatable
    if (t->metatable) {
        if (is_element_in(&gTablOutputDone, t->metatable)) {
        } else if (!is_element_in(&gTablOutputTODO, t->metatable)) {
            add_element(&gTablOutputTODO, t->metatable);
        }
        PRINT_DEBUG_LOG_WITH_DEEP("[metatable]: type:TABLE addr:0x%08X  >>REF<<\n", (int)t->metatable);
    } else {
        PRINT_DEBUG_LOG_WITH_DEEP("[metatable]:NONE\n");
    }

    //dump the nodes
    PRINT_DEBUG_LOG_WITH_DEEP("[node]:\n");
    for (i = 0; i < twoto(t->lsizenode); ++i) {
        Node* nd = &t->node[i];
        if (nd->i_key.tvk.tt == LUA_TNIL) {
            continue;
        }
        TValue* k = &nd->i_key.tvk;
        if (k->tt == LUA_TSTRING) {
            PRINT_DEBUG_LOG_WITH_DEEP("    [\"%s\"]:", getstr(&k->value.gc->ts));
        } else if (k->tt == LUA_TNUMBER) {
            PRINT_DEBUG_LOG_WITH_DEEP("    [%lf]:", k->value.n);
        } else {
            PRINT_DEBUG_LOG_WITH_DEEP("    [!!!UNKNOWN TODO!!!]:");
        }
        switch (nd->i_val.tt) {
            case LUA_TNIL:
                PRINT_DEBUG_LOG_WITH_DEEP("NIL");
                break;
            case LUA_TBOOLEAN:
                PRINT_DEBUG_LOG_WITH_DEEP("(BOOL)%d", nd->i_val.value.b);
                break;
            case LUA_TLIGHTUSERDATA:
                PRINT_DEBUG_LOG_WITH_DEEP("(LIGHTUSERDATA) addr=0x%08X", (int)nd->i_val.value.gc);
                break;
            case LUA_TNUMBER:
                PRINT_DEBUG_LOG_WITH_DEEP("(NUMBER)%lf", nd->i_val.value.n);
                break;
            case LUA_TSTRING: {
                    TString* ts = rawgco2ts(nd->i_val.value.gc);
                    PRINT_DEBUG_LOG_WITH_DEEP("(STRING)\"%s\"", getstr(ts));
                }
                break;
            case LUA_TTABLE:
                PRINT_DEBUG_LOG_WITH_DEEP("(Table) addr=0x%08X", (int)nd->i_val.value.gc);
                break;
            case LUA_TFUNCTION:
                PRINT_DEBUG_LOG_WITH_DEEP("(FUNC) func_addr:0x%08X",(int)nd->i_val.value.gc->cl.c.f);
                //{
                //    Closure* cl = &o->value.gc->cl;
                //    if (cl->c.env) {
                //        PRINT_DEBUG_LOG_WITH_DEEP("    [env] NONE.\n");
                //    }
                //    else {
                //        PRINT_DEBUG_LOG_WITH_DEEP("    [env]\n");
                //        sub_rescure_dump_table(cl->c.env, deepth + 2);
                //    }

                //    if (cl->c.isC) {
                //        PRINT_DEBUG_LOG_WITH_DEEP("    isC=true(CClosure) nupvalues=%d.\n", cl->c.nupvalues);
                //        for (int i = 0; i < cl->c.nupvalues; ++i) {
                //            PRINT_DEBUG_LOG_WITH_DEEP("    upvalues[%d]\n", i);
                //            MD_DUMP_LUA_OBJ(&cl->c.upvalue[i], deepth + 2);
                //        }
                //        break;
                //    }
                //    PRINT_DEBUG_LOG_WITH_DEEP("    isC=flase(LClosure) nupvalues=%d.\n", cl->l.nupvalues);
                //    for (int i = 0; i < cl->l.nupvalues; ++i) {
                //        PRINT_DEBUG_LOG_WITH_DEEP("    upvals[%d]\n", i);
                //        MD_DUMP_LUA_OBJ(cl->l.upvals[i]->v, deepth + 2);
                //    }
                //}
                break;
            case LUA_TUSERDATA: {
                    PRINT_DEBUG_LOG_WITH_DEEP("(USERDATA) addr:0x%08X\n", (int)nd->i_val.value.gc);
                    Udata* u = &nd->i_val.value.gc->u;

                    PRINT_DEBUG_LOG_WITH_DEEP("    .envirment addr:0x%08X >>REF<<\n", (int)u->uv.env);
                    if (is_element_in(&gTablOutputDone, u->uv.env)) {
                    } else if (!is_element_in(&gTablOutputTODO, u->uv.env)) {
                        add_element(&gTablOutputTODO, u->uv.env);
                    }

                    PRINT_DEBUG_LOG_WITH_DEEP("    .metatable addr:0x%08X >>REF<<", (int)u->uv.metatable);
                    if (is_element_in(&gTablOutputDone, u->uv.metatable)) {
                    } else if (!is_element_in(&gTablOutputTODO, u->uv.metatable)) {
                        add_element(&gTablOutputTODO, u->uv.metatable);
                    }
                }
                break;
            case LUA_TTHREAD:
                PRINT_DEBUG_LOG_WITH_DEEP("(THREAD) addr:0x%08X", (int)nd->i_val.value.gc);
                break;
            default:
                PRINT_DEBUG_LOG_WITH_DEEP("(UNKOWN!!!) addr=0x%08X", (int)nd->i_val.value.gc);
                break;
        }
        PRINT_DEBUG_LOG_WITH_DEEP("\n");
    }
    PRINT_DEBUG_LOG_WITH_DEEP("\n");
    return 0;
}



int MD_DUMP_LUA_OBJ_EASY(TValue* o, int deepth) { //不递归展开表，简化输出，更易于查看
    if (NULL == o) {
        PRINT_DEBUG_LOG_WITH_DEEP("NULL POINTER!!!!!!!!!!!!!!!!!!!!!!!!!\n");
        return -1;
    }
    ++deepth;
    switch (o->tt) {
    case LUA_TNIL:
        PRINT_DEBUG_LOG_WITH_DEEP("type:NIL\n");
        break;
    case LUA_TBOOLEAN:
        PRINT_DEBUG_LOG_WITH_DEEP("type:BOOL val:%d\n", o->value.b);
        break;
    case LUA_TLIGHTUSERDATA:
        PRINT_DEBUG_LOG_WITH_DEEP("addr=0x%08X type:LIGHTUSERDATA val:TODO\n", (int)o->value.gc);
        break;
    case LUA_TNUMBER:
        PRINT_DEBUG_LOG_WITH_DEEP("type:NUMBER val:%lf\n", o->value.n);
        break;
    case LUA_TSTRING: {
            TString* ts = rawgco2ts(o->value.gc);
            PRINT_DEBUG_LOG_WITH_DEEP("addr=0x%08X type:STRING hash:0x%08X slen:%d val:%s\n", (int)o->value.gc, ts->tsv.hash, ts->tsv.len, getstr(ts));
        }
        break;
    case LUA_TTABLE:
        sub_rescure_dump_table_easy(&o->value.gc->h, deepth);
        break;
    case LUA_TFUNCTION:
        PRINT_DEBUG_LOG_WITH_DEEP("addr=0x%08X type:FUNC val:0x%08X\n", (int)o->value.gc, (int)o->value.gc->cl.c.f);
        {
            Closure* cl = &o->value.gc->cl;
            if (cl->c.env) {
                PRINT_DEBUG_LOG_WITH_DEEP("    [env] NONE.\n");
            } else {
                PRINT_DEBUG_LOG_WITH_DEEP("    [env]: Table addr:0x%08X >>REF<<\n", (int)(cl->c.env));
                if (is_element_in(&gTablOutputDone, cl->c.env)) {
                } else if (!is_element_in(&gTablOutputTODO, cl->c.env)) {
                    add_element(&gTablOutputTODO, cl->c.env);
                }
            }

            //if (cl->c.isC) {
            //    PRINT_DEBUG_LOG_WITH_DEEP("    isC=true(CClosure) nupvalues=%d.\n", cl->c.nupvalues);
            //    for (int i = 0; i < cl->c.nupvalues; ++i) {
            //        PRINT_DEBUG_LOG_WITH_DEEP("    upvalues[%d]\n", i);
            //        MD_DUMP_LUA_OBJ(&cl->c.upvalue[i], deepth + 2);
            //    }
            //    break;
            //}
            //PRINT_DEBUG_LOG_WITH_DEEP("    isC=flase(LClosure) nupvalues=%d.\n", cl->l.nupvalues);
            //for (int i = 0; i < cl->l.nupvalues; ++i) {
            //    PRINT_DEBUG_LOG_WITH_DEEP("    upvals[%d]\n", i);
            //    MD_DUMP_LUA_OBJ(cl->l.upvals[i]->v, deepth + 2);
            //}
        }
        break;
    case LUA_TUSERDATA: {
        if (is_element_in(&gTablOutputDone, o->value.gc)) {
            PRINT_DEBUG_LOG_WITH_DEEP("the value is a USERDATA that has outputed. addr:0x%08X.\n", (int)o->value.gc);
            return 0;
        }
        add_element(&gTablOutputDone, o->value.gc);

        Udata* u = &o->value.gc->u;
        PRINT_DEBUG_LOG_WITH_DEEP("addr=0x%08X type:USERDATA len=%d, val:\n", (int)o->value.gc, u->uv.len);

        PRINT_DEBUG_LOG_WITH_DEEP("    [envirment]:\n");
        sub_rescure_dump_table(u->uv.env, deepth + 2);

        PRINT_DEBUG_LOG_WITH_DEEP("    [metatable]:\n");
        sub_rescure_dump_table(u->uv.metatable, deepth + 2);
    }
                        break;
    case LUA_TTHREAD:
        PRINT_DEBUG_LOG_WITH_DEEP("addr=0x%08X type:THREAD val:TODO\n", (int)o->value.gc);
        break;
    default:
        PRINT_DEBUG_LOG_WITH_DEEP("addr=0x%08X type:UNKNOWN!!\n", (int)o->value.gc);
        break;
    }
    return 0;
}


void MD_SHOW_STACK(lua_State* L) {
	TValue* it = L->top;
    PRINT_DEBUG_LOG_N("===OUTPUT LUA STACK==");
	while (it >= L->stack) {
        char pointStr[64] = {0};
        if (it == L->base) strcat(pointStr, "L->base ");
        if (it == L->top) strcat(pointStr, "L->top ");
        for (int i = 0; i < L->nCcalls + 1; ++i) {
            char buf2[32] = {0};
            sprintf(buf2, "b_ci[%d].base ", i);
            if (it == L->base_ci[i].base) strcat(pointStr, buf2);

            sprintf(buf2, "b_ci[%d].func ", i);
            if (it == L->base_ci[i].func) strcat(pointStr, buf2);
        }

        if (pointStr[0]) {
            PRINT_DEBUG_LOG("%-30s  --->", pointStr);
        } else {
            PRINT_DEBUG_LOG("%-30s      ", "");
        }
        if (it == L->top) { //top value do not output(invalid data)
            --it;
            PRINT_DEBUG_LOG("\n");
            continue;
        }

		switch (it->tt) {
		case LUA_TNIL:
			PRINT_DEBUG_LOG_N("stack[%d] NIL", it - L->stack);
			break;
        case LUA_TBOOLEAN:
            PRINT_DEBUG_LOG_N("stack[%d] BOOLEAN:%d", it - L->stack, it->value.b);
            break;
        case LUA_TLIGHTUSERDATA:
            PRINT_DEBUG_LOG_N("stack[%d] LIGHT USER DATA. TODO", it - L->stack);
            break;
        case LUA_TNUMBER:
            PRINT_DEBUG_LOG_N("stack[%d] NUMBER:%lf", it - L->stack, it->value.n);
            break;

		case LUA_TSTRING:
			PRINT_DEBUG_LOG_N("stack[%d] STR:\"%s\"", it - L->stack, (char*)(&it->value.gc->ts + 1));
			break;
		case LUA_TTABLE:
			PRINT_DEBUG_LOG_N("stack[%d] TBL, addr:0x%p", it - L->stack, it->value.gc);
			break;
		case LUA_TFUNCTION:
			PRINT_DEBUG_LOG_N("stack[%d] FUNC FuncName:%s(0x%p)", it - L->stack, get_func_name_by_addr(it->value.gc->cl.c.f), it->value.gc->cl.c.f);
			break;

        case LUA_TUSERDATA:
            PRINT_DEBUG_LOG_N("stack[%d] USER DATA. len=%d", it - L->stack, it->value.gc->u.uv.len);
            break;
        case LUA_TTHREAD:
            PRINT_DEBUG_LOG_N("stack[%d] USER DATA. TODO.", it - L->stack);
            break;

		default:
			PRINT_DEBUG_LOG_N("stack[%d] UNKOWN.", it - L->stack);
			break;
		}
		--it;
	}
    PRINT_DEBUG_LOG_N("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
}

int md_print_g_strt(lua_State* L) {
    global_State* g = L->l_G;
    PRINT_DEBUG_LOG_N("---g.strt.hash g->strt.size=%d----------------------------------", g->strt.size);
    for (int i = 0; i < g->strt.size; ++i) {
        TString* t = (TString*)(g->strt.hash[i]);
        if (NULL == t) {
            PRINT_DEBUG_LOG_N("g.strt.hash[% 4d] is [FREE].", i);
            continue;
        }
        
        const char* str0 = (const char*)(t + 1);
        PRINT_DEBUG_LOG_N("g.strt.hash[% 4d].next[%d], hash=0x%08X, reserved=%02d, slen=%02d, str=\"%s\"", i, 0, t->tsv.hash, t->tsv.reserved, t->tsv.len, str0);
        
        int j = 1;
        t = (TString*)t->tsv.next;
        while (t) {
            const char* str = (const char*)(t + 1);
            PRINT_DEBUG_LOG_N("                  next[%d], hash=0x%08X, reserved=%02d, slen=%02d, str=\"%s\"", j, t->tsv.hash, t->tsv.reserved, t->tsv.len, str);
            t = (TString*)t->tsv.next;
            j += 1;
        }
    }
    PRINT_DEBUG_LOG_N("");
    return 0;
}

int md_print_g_tmname(lua_State* L) {
    PRINT_DEBUG_LOG_N("---g.tmname(tag method)----------------------------------");
    global_State* g = L->l_G;
    TString** pTS = g->tmname;
    for (int k = 0; k < TM_N; ++k) {
        TString* t = pTS[k];
        const char* str = (const char*)(t + 1);
        PRINT_DEBUG_LOG_N("g.tmname[%02d], hash=0x%08X, slen=%02d, str=\"%s\"", k, t->tsv.hash, t->tsv.len, str);
    }
    PRINT_DEBUG_LOG_N("");
    return 0;
}

int md_print_g_rootgc(lua_State* L) {
    PRINT_DEBUG_LOG_N("---g.rootgc----------------------------------");
    GCObject* o = L->l_G->rootgc;
    int cnt = 0;
    PRINT_DEBUG_LOG_N("L->l_G->rootgc -> 0x%08X", (int)o);
    while (o) {
        if (o == (GCObject*)L) {
            PRINT_DEBUG_LOG("[!! L !!]");
        }
        PRINT_DEBUG_LOG_N("addr:0x%p, marked=0x%02X, next -> 0x%08X", o, o->gch.marked, (int)o->gch.next);
        TValue t;
        t.tt = o->gch.tt;
        t.value.gc = o;
        MD_DUMP_LUA_OBJ(&t, 0);
        ++cnt;
        o = o->gch.next;
    }
    PRINT_DEBUG_LOG_N("rootgc cnt=%d\n", cnt);

    //PRINT_DEBUG_LOG_N("---g.rootgc----------------------------------");
    //global_State* g = L->l_G;
    //GCObject* o = g->rootgc;
    //int cnt = 0;
    ////为了差分方便，倒序输出
    //while (o) {
    //    ++cnt;
    //    o = o->gch.next;
    //}
    //TValue* arr = (TValue*)malloc(sizeof(TValue) * cnt);
    //o = g->rootgc;
    //int idx = 0;
    //while (o) {
    //    arr[cnt - idx - 1].value.gc = o;
    //    arr[cnt - idx - 1].tt = o->gch.tt;
    //    ++idx;
    //    o = o->gch.next;
    //}

    //for (int i = 0; i < cnt; ++i) {
    //    PRINT_DEBUG_LOG_N("idx[%d], addr:0x%p type=%s, marked=0x%02X", i, arr[i].value.gc, get_tt_name(arr[i].tt), arr[i].value.gc->gch.marked);
    //    MD_DUMP_LUA_OBJ(&arr[i], 0);
    //}
    //free(arr);
    //PRINT_DEBUG_LOG_N("");
    return 0;
}

int md_print_ci(lua_State* L) {
    PRINT_DEBUG_LOG_N("---L->ci---L->nCcalls=%d-------------------------------", L->nCcalls);

    for (int i = 0; i < L->nCcalls + 1; ++i) {
        CallInfo* ci = &L->base_ci[i];
        if (ci == L->ci) {
            PRINT_DEBUG_LOG(" [!!current call!!]");
        }
        PRINT_DEBUG_LOG_N("call idx[%d], base -> stack[%d], func -> stack[%d], top -> stack[%d], savedpc=0x%08X, nresults=%d, tailcalls=%d", i, ci->base - L->stack, ci->func - L->stack, ci->top - L->stack, (int)ci->savedpc, ci->nresults, ci->tailcalls);
    }
    return 0;
}



